package com.gitee.wsl.compose.ui.richtext.edit.model.span.style

import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.geometry.RoundRect
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.graphics.drawscope.Fill
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.TextLayoutResult
import androidx.compose.ui.text.TextRange
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.sp
import androidx.compose.ui.util.fastForEachIndexed
import com.gitee.wsl.compose.ui.richtext.edit.annotation.ExperimentalRichTextApi
import com.gitee.wsl.compose.ext.text.getBoundingBoxes
import com.gitee.wsl.compose.ui.richtext.edit.model.RichTextConfig
import com.gitee.wsl.compose.ui.richtext.edit.model.TextPaddingValues


@OptIn(ExperimentalRichTextApi::class)
class Code(
    private val cornerRadius: TextUnit = 8.sp,
    private val strokeWidth: TextUnit = 1.sp,
    private val padding: TextPaddingValues = TextPaddingValues(horizontal = 2.sp, vertical = 2.sp)
) : RichSpanStyle {

    override val spanStyle: (RichTextConfig) -> SpanStyle = {
        SpanStyle(color = it.codeSpanColor,)
    }

    override fun DrawScope.drawCustomStyle(
        layoutResult: TextLayoutResult,
        textRange: TextRange,
        richTextConfig: RichTextConfig,
        topPadding: Float,
        startPadding: Float,
    ) {
        val path = Path()
        val backgroundColor = richTextConfig.codeSpanBackgroundColor
        val strokeColor = richTextConfig.codeSpanStrokeColor
        val cornerRadius = CornerRadius(cornerRadius.toPx())
        val boxes = layoutResult.getBoundingBoxes(
            startOffset = textRange.start,
            endOffset = textRange.end,
            flattenForFullParagraphs = true
        )

        boxes.fastForEachIndexed { index, box ->
            path.addRoundRect(
                RoundRect(
                    rect = box.copy(
                        left = box.left - padding.horizontal.toPx() + startPadding,
                        right = box.right + padding.horizontal.toPx() + startPadding,
                        top = box.top - padding.vertical.toPx() + topPadding,
                        bottom = box.bottom + padding.vertical.toPx() + topPadding,
                    ),
                    topLeft = if (index == 0) cornerRadius else CornerRadius.Zero,
                    bottomLeft = if (index == 0) cornerRadius else CornerRadius.Zero,
                    topRight = if (index == boxes.lastIndex) cornerRadius else CornerRadius.Zero,
                    bottomRight = if (index == boxes.lastIndex) cornerRadius else CornerRadius.Zero
                )
            )
            drawPath(
                path = path,
                color = backgroundColor,
                style = Fill
            )
            drawPath(
                path = path,
                color = strokeColor,
                style = Stroke(
                    width = strokeWidth.toPx(),
                )
            )
        }
    }

    override val acceptNewTextInTheEdges: Boolean = true

    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (other !is Code) return false

        if (cornerRadius != other.cornerRadius) return false
        if (strokeWidth != other.strokeWidth) return false
        if (padding != other.padding) return false

        return true
    }

    override fun hashCode(): Int {
        var result = cornerRadius.hashCode()
        result = 31 * result + strokeWidth.hashCode()
        result = 31 * result + padding.hashCode()
        return result
    }
}

@OptIn(ExperimentalRichTextApi::class)
fun RichSpanStyle.code(cornerRadius: TextUnit = 8.sp,
                       strokeWidth: TextUnit = 1.sp,
                       padding: TextPaddingValues = TextPaddingValues(horizontal = 2.sp, vertical = 2.sp)
)= Code(cornerRadius, strokeWidth, padding)